/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2019-2019.
 * Description: rtos fix fsl erratum a-006184
 * Author: Jiangfeng Xiao
 * Create: 2019-09-06
 */

#include <linux/module.h>
#include <asm/time.h>

#define A_006184_HRTIMER_PERIOD 500000000 /* 500ms */
#define A_006184_WDTP(x) ((((x) & 0x3) << 30) | (((x) & 0x3c) << 15))

static struct hrtimer hrtimer[NR_CPUS];

static enum hrtimer_restart a_006184_feed_dog(struct hrtimer *ht)
{
	ktime_t now, interval;

	mtspr(SPRN_TSR, TSR_ENW | TSR_WIS);

	now = hrtimer_cb_get_time(ht);
	interval = ktime_set(0, A_006184_HRTIMER_PERIOD);
	hrtimer_forward(ht, now, interval);

	return HRTIMER_RESTART;
}

static void a_006184_start_hrtimer(void)
{
	int cpu = smp_processor_id();
	ktime_t interval;

	hrtimer_init(&hrtimer[cpu], CLOCK_MONOTONIC, HRTIMER_MODE_REL);
	hrtimer[cpu].function = a_006184_feed_dog;

	interval = ktime_set(0, A_006184_HRTIMER_PERIOD);
	hrtimer_start(&hrtimer[cpu], interval, HRTIMER_MODE_REL);
}

int rtos_set_ppc_tcr_wp(u32 period)
{
	u32 tcr;
	u32 wp;

	if (period > 63) {
		pr_err("%s period is out of range[0, 63]\n", __func__);
		return -EINVAL;
	}

	mtspr(SPRN_TSR, TSR_ENW | TSR_WIS);
	wp = A_006184_WDTP(period);
	tcr = mfspr(SPRN_TCR);
	tcr &= ~A_006184_WDTP(63);
	tcr |= TCR_WIE | wp;
	mtspr(SPRN_TCR, tcr);
	pr_info("%s success to set ppc-tcr-wp %x\n", __func__, period);

	return 0;
}
EXPORT_SYMBOL(rtos_set_ppc_tcr_wp);

static u32 get_wdt_period(u32 sec)
{
	u64 tb;
	u32 msb;

	tb = (u64)tb_ticks_per_sec * (u64)sec;
	msb = 0;
	while (tb) {
		tb >>= 1;
		msb++;
	}

	return (64 - msb);
}


static void a_006184_dog_init(void)
{
	(void)rtos_set_ppc_tcr_wp(get_wdt_period(1));
}

static void a_006184_bugfix_init(void *data)
{
	a_006184_dog_init();
	a_006184_start_hrtimer();
}

static int a_006184_init(void)
{
	on_each_cpu(a_006184_bugfix_init, NULL, 1);
	return 0;
}

core_initcall(a_006184_init);

MODULE_DESCRIPTION("rtos fix fsl erratum a-006184");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("HUAWEI TECHNOLOGIES CO., LTD.");
